AHOI2017初中组 guide

题意

对于一张有向图,每条边有两个边权,有两套GPS会分别按两个边权计算所在到终点的最短路,如果你不按其中一套GPS说的路走,就会产生1的抱怨,求从起点到终点的最少抱怨数

题解

因为每次两套GPS都会判断当前要去的点是否在当前点到终点的最短路上,所以我们可以反向建边,跑出每套GPS从终点到每个点的最短路,然后我们重新建图,依次枚举原图的每条边,将边权赋值为2,如果要去的点在某套GPS的最短路上,边权减一.最后跑一遍最短路就可以得出$ans$

  • 反向建边
  • 反向SPFA
  • 边重构
  • 反向SPFA
  • $ans$
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e5+10;
int n,m,head[maxn],num;
int dis[maxn],dis1[maxn],di[maxn];
bool use[maxn];
struct fy{int from,to,d,dd,e,next;}q[maxn*5];
void add(int a,int b,int c,int d)
{
    q[++num]=(fy){a,b,c,d,0,head[a]};head[a]=num;
}
void sp()
{
    memset(dis,0x3f,sizeof dis);dis[n]=0;
    memset(dis1,0x3f,sizeof dis1);dis1[n]=0;
    queue<int>qq;qq.push(n);use[n]=true;
    while(!qq.empty())
    {
        int a=qq.front();qq.pop();use[a]=false;
        for(int i=head[a];i;i=q[i].next)
        {
            int b=q[i].to;
            if(dis[b]>dis[a]+q[i].d)
            {
                dis[b]=dis[a]+q[i].d;
                if(!use[b]){use[b]=true;qq.push(b);}
            }
        }
    }
    qq.push(n);use[n]=true;
    while(!qq.empty())
    {
        int a=qq.front();qq.pop();use[a]=false;
        for(int i=head[a];i;i=q[i].next)
        {
            int b=q[i].to;
            if(dis1[b]>dis1[a]+q[i].dd)
            {
                dis1[b]=dis1[a]+q[i].dd;
                if(!use[b]){use[b]=true;qq.push(b);}
            }
        }
    }
}
void sp1()
{
    memset(di,0x3f,sizeof di);di[n]=0;
    queue<int>qq;qq.push(n);use[n]=true;
    while(!qq.empty())
    {
        int a=qq.front();qq.pop();use[a]=false;
        for(int i=head[a];i;i=q[i].next)
        {
            int b=q[i].to;
            if(di[b]>di[a]+q[i].e)
            {
                di[b]=di[a]+q[i].e;
                if(!use[b]){use[b]=true;qq.push(b);}
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);int a,b,c,d;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        add(b,a,c,d);
    }
    sp();//反向SPFA
    for(int i=1;i<=num;i++)//边重构
    {
        d=2;a=q[i].from;b=q[i].to;
        if(dis[b]==dis[a]+q[i].d)d--;
        if(dis1[b]==dis1[a]+q[i].dd)d--;
        q[i].e=d;
    }
    sp1();
    printf("%d\n",di[1]);
    return 0;
}